{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "91c0b9fd-213a-4da8-b84b-c766b424716c",
   "metadata": {},
   "source": [
    "# Milvus + Llama-Index: Enhancing OpenAI Assistant Agent with a Custom Retriever\n",
    "\n",
    "This shows how to enhance [Llama-Index](https://www.llamaindex.ai/)'s agent built on top of the [OpenAI Assistant API](https://platform.openai.com/docs/assistants/overview) with retriever tool customized by [Milvus](https://zilliz.com).\n",
    "\n",
    "## Preparation\n",
    "\n",
    "### 1. Install dependencies"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b363b294",
   "metadata": {},
   "outputs": [],
   "source": [
    "! pip install -q llama-index 'milvus[client]' 'openai>=1.2.0' transformers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "958680bb",
   "metadata": {},
   "source": [
    "### 2. Start Milvus Service\n",
    "\n",
    "There are 2 options to start a Milvus service:\n",
    "\n",
    "- [Zilliz Cloud](https://zilliz.com/cloud): Zilliz provides cloud-native service for Milvus. It simplifies the process of deploying and scaling vector search applications by eliminating the need to create and maintain complex data infrastructure. [Get Started Free!](https://cloud.zilliz.com/signup)\n",
    "- [Open Source Milvus](https://milvus.io): You can install the open source Milvus using either Docker Compose or on Kubernetes.\n",
    "\n",
    "Here, we use [Milvus Lite](https://milvus.io/docs/milvus_lite.md) to start with a lightweight version of Milvus, which works seamlessly with Google Colab and Jupyter Notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "2aaa83de",
   "metadata": {},
   "outputs": [],
   "source": [
    "from milvus import default_server\n",
    "\n",
    "# default_server.cleanup()  # Optional, run this line if you want to cleanup previous data\n",
    "default_server.start()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b695901d",
   "metadata": {},
   "source": [
    "### 3. Download example data\n",
    "\n",
    "You can use any file(s) to build the knowledge base.\n",
    "We will use a SEC file [uber_2021.pdf](https://github.com/run-llama/llama_index/blob/main/docs/examples/data/10k/uber_2021.pdf) as an example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "dbecdfda",
   "metadata": {},
   "outputs": [],
   "source": [
    "! wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/10k/uber_2021.pdf' -O 'uber_2021.pdf'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1661a1c6",
   "metadata": {},
   "source": [
    "## Getting Started\n",
    "\n",
    "### 1. Set up Environment\n",
    "\n",
    "You need to set up some environment variables, for example, passing your [OpenAI API Key](https://beta.openai.com/account/api-keys).\n",
    "Please note that your OpenAI account should have the accessibility and enough quota available for the model [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "08081fca-0274-42ae-a777-4b66d36d691c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from getpass import getpass\n",
    "\n",
    "os.environ['TOKENIZERS_PARALLELISM'] = 'false'\n",
    "os.environ['OPENAI_API_KEY'] = getpass('Enter OpenAI API Key:')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "124e19e5",
   "metadata": {},
   "source": [
    "### 2. Customize Strategies\n",
    "\n",
    "In this step, we will define some strategies to be used:\n",
    "\n",
    "- Chunking: configure the text splitter (e.g. `chunk_size`)\n",
    "- Embedding: choose embedding model (e.g. [`BAAI/bge-small-en`](https://huggingface.co/BAAI/bge-small-en)) and its provider (e.g. [HuggingFace](https://huggingface.co/models), [OpenAI](https://platform.openai.com/docs/guides/embeddings)).\n",
    "- LLM: select LLM model (e.g. `gpt-4-1106-preview`) and set up model parameters (e.g. `temperature`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "4712df4c",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/mengjia.gu/anaconda3/envs/develop/lib/python3.8/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    }
   ],
   "source": [
    "from llama_index.embeddings import HuggingFaceEmbedding\n",
    "from llama_index.llms import OpenAI\n",
    "from llama_index.vector_stores import MilvusVectorStore\n",
    "from llama_index import StorageContext, ServiceContext\n",
    "\n",
    "\n",
    "llm = OpenAI(model='gpt-4-1106-preview')\n",
    "embed_model = HuggingFaceEmbedding(model_name='BAAI/bge-small-en', cache_folder='./tmp/models', device='cpu')\n",
    "service_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model, chunk_size=350)\n",
    "\n",
    "vector_store = MilvusVectorStore(\n",
    "    uri=f'http://localhost:{default_server.listen_port}',\n",
    "    # token='',  # required for Zilliz Cloud\n",
    "    dim=384,  # the value changes with embedding model\n",
    "    overwrite=True  # drop table if exist and then create\n",
    "    )\n",
    "storage_context = StorageContext.from_defaults(vector_store=vector_store)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f1147b1",
   "metadata": {},
   "source": [
    "### 3. Ingest Document(s)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c306c18a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index import SimpleDirectoryReader, VectorStoreIndex\n",
    "\n",
    "# Load document(s)\n",
    "docs = SimpleDirectoryReader(input_files=['./uber_2021.pdf']).load_data()\n",
    "\n",
    "# Build index\n",
    "vector_index = VectorStoreIndex.from_documents(docs, storage_context=storage_context, service_context=service_context)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1ee425a1",
   "metadata": {},
   "source": [
    "### 4. Define Agent & Tool(s)\n",
    "\n",
    "In order integrate the vector store index with agent, we need to define the index as a Retriever Tool.\n",
    "The agent will be able to recognize the retriever via the tool's name and description in metadata."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "612254fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.tools import RetrieverTool, ToolMetadata\n",
    "\n",
    "milvus_tool = RetrieverTool(\n",
    "    retriever=vector_index.as_retriever(similarity_top_k=3),  # retrieve top_k results\n",
    "    metadata=ToolMetadata(\n",
    "        name=\"CustomRetriever\",\n",
    "        description='Retrieve relevant information from provided documents.'\n",
    "    ),\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d16e580",
   "metadata": {},
   "source": [
    "Then let's define the agent powered by OpenAI's Assistants API.\n",
    "To create a agent, we will define its role, give instructions, and provide tools.\n",
    "Here we will make LLM thinking itself a SEC analyst, with Milvus retriever as an available tool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "dd27eee6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.agent import OpenAIAssistantAgent\n",
    "\n",
    "agent = OpenAIAssistantAgent.from_new(\n",
    "    name='SEC Analyst',\n",
    "    instructions='You are a QA assistant designed to analyze sec filings.',\n",
    "    tools=[milvus_tool],\n",
    "    verbose=True,\n",
    "    run_retrieve_sleep_time=1.0\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22a35004",
   "metadata": {},
   "source": [
    "## Try it out!\n",
    "\n",
    "Now the agent is ready as a SEC analyst. It is able to respond to users based off documents loaded into Milvus.\n",
    "\n",
    "With `verbose=True`, you are able to what information are retrieved when the agent's answering your question."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "8f88c602",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== Calling Function ===\n",
      "Calling function: CustomRetriever with args: {\"input\":\"Uber Technologies, Inc. annual revenue growth 2021\"}\n",
      "Got output: page_label = 77\n",
      "file_name = uber_2021.pdf\n",
      "file_path = uber_2021.pdf\n",
      "UBER TECHNOLOGIES, INC.CONSOLIDATED STATEMENTS OF\n",
      " OPERATIONS(In millions, except share amounts which are ref\n",
      "lected in thousands, and per share amounts)Year Ended December 31,\n",
      "2019\n",
      "2020 2021 Revenue\n",
      "$ 13,000 $ 11,139 $ 17,455 Costs and expenses\n",
      "Cost of revenue, exclusive of dep\n",
      "reciation and amortization shown separately below6,061 5,154 9,351 Operations and support\n",
      "2,302 1,819 1,877 Sales and marketing\n",
      "4,626 3,583 4,789 Research and development\n",
      "4,836 2,205 2,054 General and administrative\n",
      "3,299 2,666 2,316 Depreciation and amortization\n",
      "472 575 902 Total costs and expenses\n",
      "21,596 16,002 21,289 Loss from operations\n",
      "(8,596) (4,863) (3,834) Interest expense\n",
      "(559) (458) (483) Other income (expense), net\n",
      "722 (1,625) 3,292 Loss before income taxes and loss from equity me\n",
      "thod investments(8,433) (6,946) (1,025) Provision for (benefit fro\n",
      "m) income taxes45 (192) (492) Loss from equity method invest\n",
      "ments(34) (34) (37) Net loss including non-controlling interests\n",
      "(8,512) (6,\n",
      "\n",
      "page_label = 57\n",
      "file_name = uber_2021.pdf\n",
      "file_path = uber_2021.pdf\n",
      "(61) %(3) % Totals of percentage of \n",
      "revenues may not foot due to rounding.Comparison of the Years Ended December 31, 2020 and 2021\n",
      "Revenue\n",
      "Year Ended December 31,\n",
      "2020 to 2021 % Change\n",
      "(In millions, except percentages) 2020 2021 Revenue\n",
      "$ 11,139 $ 17,455 57 %2021\n",
      " Compared to 2020Revenue\n",
      " increased $6.3 billion, or 57%, primarily attributable to an increase in Gross Bookings of 56%, or 53% on a constant currency basis. The increase inGross\n",
      " Bookings was primarily driven by an increase in Delivery Gross Bookings of 71%, or 66% on a constant currency basis, due to an increase in food deliveryorders\n",
      " and  higher  basket  sizes  as  a  result  of  stay-at-home  order  demand  related  to  COVID-19,  as  well  as  continued  expansion  across  U.S.  and  internationalmarkets.\n",
      " The increase was also driven by Mobility Gross Bookings growth of 38%, or 36% on a constant currency basis, due to increases in Trip volumes as thebusiness\n",
      " recovers from the impacts of COVID-19.\n",
      "\n",
      "page_label = 82\n",
      "file_name = uber_2021.pdf\n",
      "file_path = uber_2021.pdf\n",
      "UBER TECHNOLOGIES, INC.CONSOLIDATED STATEMENTS OF\n",
      " CASH FLOWS(In millions)\n",
      "Year Ended December 31,\n",
      "2019\n",
      "2020 2021 Cash flows from operating activities\n",
      "Net loss including non-controll\n",
      "ing interests$ (8,512) $ (6,788) $ (570) Adjustments to reconcile n\n",
      "et loss to net cash used in operating activities:Depreciation and amortization\n",
      "472 575 902 Bad debt expense\n",
      "92 76 109 Stock-based compensation\n",
      "4,596 827 1,168 Gain on extinguishment of conver\n",
      "tible notes and settlement of derivatives(444) — — Gain from sale of investm\n",
      "ents— — (413) Gain on business divestitures, net\n",
      "— (204) (1,684) Deferred income taxes\n",
      "(88) (266) (692) Impairment of debt and \n",
      "equity securities— 1,690 — Impairments of goodwill, long\n",
      "-lived assets and other assets— 404 116 Loss from equity method invest\n",
      "ments34 34 37 Unrealized (gain) loss on deb\n",
      "t and equity securities, net(2) 125 (1,142) Unrealized foreign cur\n",
      "rency transactions16 48 38 Other\n",
      "15 2 4 Change in assets and liabili\n",
      "ties, net of impact of business acquisitions and disposals:Accounts receivable\n",
      "(407) 142 (597) Prepaid expenses and other asse\n",
      "ts(478) 94 (236) Collateral held by insurer\n",
      "(1,\n",
      "\n",
      "\n",
      "========================\n"
     ]
    }
   ],
   "source": [
    "# print('Thread id:', agent.thread_id)\n",
    "response = agent.chat('''What was Uber's revenue growth in 2021?''')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "725e09bf",
   "metadata": {},
   "source": [
    "Check the agent's answer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "06dcf5dd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Uber's revenue growth in 2021 was 57 percent compared to 2020, with revenue increasing from $11,139 million in 2020 to $17,455 million in 2021.\n"
     ]
    }
   ],
   "source": [
    "print(str(response))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd183e6f",
   "metadata": {},
   "source": [
    "## Optional\n",
    "\n",
    "For Milvus-Lite, stop the service at the end."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "4a32cd16",
   "metadata": {},
   "outputs": [],
   "source": [
    "default_server.stop()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "develop",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
